package top.yvyan.guettable.bean;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import top.yvyan.guettable.Gson.ExamScoreNew;
import top.yvyan.guettable.Gson.ProgramModel;
import top.yvyan.guettable.Gson.Semester;
import top.yvyan.guettable.data.MoreData;

public class GradesBean {


  public List<Score> scoreList = new ArrayList<>();

  public Map<String, List<Score>> courseMap = new HashMap<>();

  public Map<String, List<Score>> yearMap = new HashMap<>();

  public List<String> years = new ArrayList<>();

  public Map<String, Semester> semesterMap;
  public List<String> semesterIdList;

  public Map<String, Boolean> creditCourse = new HashMap<>();

  public UserOverride userOverride = new UserOverride();

  public static class UserOverride {
    public Map<String, Boolean> creditCourse; // 覆盖学分计入状态
    public Map<String, Boolean> preferClass;

    public UserOverride() {
      this.creditCourse = new HashMap<>();
      this.preferClass = new HashMap<>();
    }
  }

  public ProgramModel model;

  public static class Score implements Cloneable {
    public Double credits; // 学分
    public String courseCode;
    public String courseName; // 课程名称
    public String courseProperty;

    public String semesterName; // 学期
    public Semester semester; // 学期
    public double score;
    public String gaGrade;

    public Integer id;

    public boolean consider; // 是否计入学分绩

    @NonNull
    @Override
    public Score clone() {
      try {
        return (Score) super.clone();
      } catch (CloneNotSupportedException e) {
        throw new AssertionError();
      }
    }
  }

  public double parseGaGrade(String gaGrade) {
    switch (gaGrade) {
      case "优":
        return 95;
      case "良":
        return 85;
      case "中":
        return 75;
      case "合格":
      case "及格":
        return 65;
      case "不合格":
      case "不及格":
        return 40;
      default:
        try {
          return Double.parseDouble(gaGrade);
        } catch (Exception ignored) {

        }
    }
    return 100;
  }

  public GradesBean(ExamScoreNew examscorenew, ProgramModel model, Map<String, Semester> semesterMap, UserOverride userOverride) {
    if (userOverride != null) {
      this.userOverride = userOverride;
    }
    this.semesterMap = semesterMap;
    this.semesterIdList = new ArrayList<>(examscorenew.semesterId2studentGrades.keySet());
    for (Map.Entry<String, List<ExamScoreNew.ScoreDetail>> scores : examscorenew.semesterId2studentGrades.entrySet()) {
      for (ExamScoreNew.ScoreDetail score : scores.getValue()) {
        Score gradeScore = new Score();
        gradeScore.id = score.id;
        gradeScore.credits = score.credits;
        gradeScore.courseCode = score.courseCode;
        gradeScore.courseName = score.courseName;
        gradeScore.courseProperty = score.courseProperty;
        gradeScore.semesterName = score.semesterName;
        gradeScore.semester = semesterMap.get(scores.getKey());
        gradeScore.score = parseGaGrade(score.gaGrade);
        gradeScore.gaGrade = score.gaGrade;
        gradeScore.consider = false;
        this.scoreList.add(gradeScore);
      }
    }
    this.model = model;
  }

  public void fallbackRule() {
    for (Score score : this.scoreList) {
      if (creditCourse.get(score.courseCode) == null) {
        creditCourse.put(score.courseCode,!score.courseProperty.equals("通识教育") && !score.courseProperty.equals("专业任选"));
      }
    }
  }

  public void initRule() {
    creditCourse = new HashMap<>();
    if (this.model != null) {
      try {
        List<ProgramModel.Module.Course> courses = this.model.getAllCourse();
        for (ProgramModel.Module.Course course : courses) {
          creditCourse.put(course.code, course.creditsCourse);
        }
      } catch (Exception ignore) {
      }
    }
    fallbackRule();
    if (this.userOverride == null || this.userOverride.preferClass == null || this.userOverride.creditCourse == null) {
      this.userOverride = new UserOverride();
    }
    creditCourse.putAll(userOverride.creditCourse);
    creditCourse.put("教务系统-课程表APP内部课程",true);
  }

  public void initStruct() {
    this.initRule();
    courseMap = new HashMap<>();
    yearMap = new HashMap<>();
    years = new ArrayList<>(List.of("教务系统-所有","所有"));


    if (semesterIdList == null || semesterIdList.isEmpty()) { // 兼容旧版本
      semesterIdList = new ArrayList<>(semesterMap.keySet());
    }

    // 按时间序排序学期列表
    Collections.sort(semesterIdList, (a, b) -> {
      Semester semesterA = semesterMap.get(a);
      Semester semesterB = semesterMap.get(b);
      Long startTimeA = Long.MAX_VALUE;
      Long startTimeB = Long.MAX_VALUE;
      if (semesterA != null) {
        startTimeA = semesterA.getStartTime();
      }
      if (semesterB != null) {
        startTimeB = semesterB.getStartTime();
      }
      return startTimeA.compareTo(startTimeB);
    });

    for (String semesterId : semesterIdList) {
      Semester semester = semesterMap.get(semesterId);
      if (semester != null) {
        if (!years.contains(semester.schoolYear)) {
          years.add(semester.schoolYear);
        }
        yearMap.put(semester.schoolYear, new ArrayList<>());
      }
    }

    // 按学期归类成绩单
    List<Score> globalList = new ArrayList<>();
    for (Score score : scoreList) {
      List<Score> sameschoolYearList = yearMap.get(score.semester.schoolYear);
      if (sameschoolYearList != null) {
        sameschoolYearList.add(score.clone());
      } else {
        sameschoolYearList = new ArrayList<>();
        sameschoolYearList.add(score.clone());
        yearMap.put(score.semester.schoolYear, sameschoolYearList);
      }
      globalList.add(score);
    }
    yearMap.put("所有", globalList);
    List<Score> creditFromSystem = new ArrayList<>();
    if (this.model != null) { 
      Score gradeScore = new Score();
      gradeScore.id=-32767;
      gradeScore.consider=true;
      gradeScore.semesterName = "所有学年";
      gradeScore.semester = null;
      gradeScore.courseProperty = "课程表APP内部课程";
      gradeScore.credits = 1.0;
      gradeScore.gaGrade = model.creditFromSystem.toString();
      gradeScore.score = model.creditFromSystem;
      gradeScore.courseName="教务系统数据";
      gradeScore.courseCode="教务系统-课程表APP内部课程";
      creditFromSystem.add(gradeScore);
    }
    yearMap.put("教务系统-所有", creditFromSystem);
  }

  public void calcGrades() {
    // 按学期遍历成绩单，选择计入学分绩的课程 (1.用户倾向课程 2.学期内同课程代码的最高分课程)
    for (Map.Entry<String, List<Score>> yearScores : yearMap.entrySet()) {
      Map<String, List<Score>> courseMap = new HashMap<>();
      List<Score> Scores = yearScores.getValue();
      for (Score score : yearScores.getValue()) {

        score.consider = false;// 重置计入状态为 false

        List<Score> sameCourseCodeList;
        // 按课程代码分类
        if ((sameCourseCodeList = courseMap.get(score.courseCode)) != null) {
          sameCourseCodeList.add(score);
        } else {
          sameCourseCodeList = new ArrayList<>();
          sameCourseCodeList.add(score);
          courseMap.put(score.courseCode, sameCourseCodeList);
        }
      }

      // 存储全局课程映射
      if (yearScores.getKey().equals("所有")) {
        this.courseMap = courseMap;
      }

      Scores.clear(); // 清空成绩项，重新应用排序后的列表

      // 排序规则查询 Map
      Map<String, Long> courseTimeMap = new HashMap<>(); // 课程时间
      Map<String, Double> courseScoreMap = new HashMap<>(); // 课程成绩

      // 选择计入学分绩的成绩项，并排序信息
      for (Map.Entry<String, List<Score>> coursesEntry : courseMap.entrySet()) {
        List<Score> scores = coursesEntry.getValue();
        String courseCode = coursesEntry.getKey();
        // 课程内部按成绩排序
        Collections.sort(scores, (a, b) -> (int) (b.score - a.score));

        Boolean consider = creditCourse.get(courseCode); // 该课程是否计入学分绩

        Long courseTime = Long.MAX_VALUE;
        double courseScore = 0.0;
        // 选择计入学分绩的成绩项
        if (consider != null && consider) {
          boolean flag = false;
          for (Score score : scores) {
            if (score.id != null) {
              Boolean prefer = userOverride.preferClass.get(score.id.toString());
              if (prefer != null && prefer) {
                score.consider = true;
                flag = true;
                break;
              }
            }
          }
          if (!flag && !scores.isEmpty()) {
            scores.get(0).consider = true;
          }
        }

        for (Score score : scores) {
          // 获取课程时间，以最晚的成绩周期为准
          if (score.semester != null) {
            Long scoreTime = score.semester.getStartTime();
            if (courseTime == Long.MAX_VALUE) {
              courseTime = scoreTime;
            } else if (scoreTime > courseTime) {
              courseTime = scoreTime;
            }
          }
          if (score.score > courseScore) {
            courseScore = score.score;
          }
        }
        courseTimeMap.put(courseCode, courseTime);
        courseScoreMap.put(courseCode, courseScore);
      }

      List<String> courseList = new ArrayList<>(courseMap.keySet()); // 获取课程列表

      // 应用排序规则，课程项之间按照 1.时间 2.最高成绩 的优先级进行排序
      Collections.sort(courseList, (a, b) -> {
        Long courseATime = courseTimeMap.get(a);
        Long courseBTime = courseTimeMap.get(b);
        if (courseATime == null) {
          courseATime = Long.MAX_VALUE;
        }
        if (courseBTime == null) {
          courseBTime = Long.MAX_VALUE;
        }

        if (!courseATime.equals(courseBTime)) {
          return courseATime > courseBTime ? 1 : -1;
        }

        Double courseAScore = courseScoreMap.get(a);
        Double courseBScore = courseScoreMap.get(b);

        if (courseAScore == null) {
          courseAScore = 0.0;
        }
        if (courseBScore == null) {
          courseBScore = 0.0;
        }

        return courseBScore.compareTo(courseAScore);

      });

      for (String courseCode : courseList) {
        // 按照排序规则重新应用列表，课程内部已按成绩排序
        List<Score> scores = courseMap.get(courseCode);
        if (scores != null) {
          Scores.addAll(scores);
        }
      }

    }
  }


  /**
   * 切换用户自定义规则
   *
   * @param yearIndex  学年
   * @param scoreIndex 成绩索引
   */
  public void switchCustomRule(int yearIndex, int scoreIndex) {
    switchCustomRuleInternal(yearIndex, scoreIndex);
    initRule();
    calcGrades();
    MoreData.setGradesBean(this);
  }

  private void switchCustomRuleInternal(int yearIndex, int scoreIndex) {
    if (userOverride == null || userOverride.creditCourse == null || userOverride.preferClass == null) {
      return;
    }
    if (yearIndex >= years.size()) return;
    String year = years.get(yearIndex);
    List<Score> yearScores = yearMap.get(year);
    if (yearScores == null) return;
    if (scoreIndex >= yearScores.size()) return;
    Score score = yearScores.get(scoreIndex);
    if (score == null) return;
    if (score.id != null && score.courseCode != null) {
      if (score.consider) { // 若成绩项计入，切换为不计入状态
        List<Score> sameCourseScores = courseMap.get(score.courseCode);
        if (sameCourseScores == null) return;
        // 有 prefer 标记则移除 prefer 标记
        userOverride.preferClass.remove(score.id.toString());
        Boolean creditCourse = userOverride.creditCourse.get(score.courseCode);
        if (creditCourse != null) { // 有 creditCourse 标记则清除 creditCourse 标记
          userOverride.creditCourse.remove(score.courseCode);
        } else {
          // 没有 creditCourse 标记则设置 creditCourse = false
          userOverride.creditCourse.put(score.courseCode, false);
        }
      } else { // 若成绩项不计入，切换为计入状态
        Boolean creditCourse = this.creditCourse.get(score.courseCode);
        if (creditCourse != null && !creditCourse) {
          // 若课程不计入，添加用户计入规则
          Boolean userCreditCourse = userOverride.creditCourse.get(score.courseCode);
          if (userCreditCourse != null) { // 有 creditCourse 标记则清除 creditCourse 标记
            userOverride.creditCourse.remove(score.courseCode);
          } else {
            // 没有 creditCourse 标记则设置 creditCourse = true
            userOverride.creditCourse.put(score.courseCode, true);
          }
        }
        List<Score> sameCourseScores = courseMap.get(score.courseCode);
        if (sameCourseScores == null) return;
        for (Score sameCourseScore : sameCourseScores) {
          // 清除所有 prefer 标记
          if (sameCourseScore.id == null) continue;
          userOverride.preferClass.remove(sameCourseScore.id.toString());
        }
        userOverride.preferClass.put(score.id.toString(), true); // 设置 prefer = true
      }
    }
  }

  public static double calcGrade(List<Score> scores) {
    double creditSum = 0;
    double scoreSum = 0;
    for (Score score : scores) {
      if (!score.consider) continue;
      creditSum += score.credits;
      scoreSum += score.score * score.credits;
    }
    return scoreSum / creditSum;
  }

  public static double calcCreditSum(List<Score> scores) {
    double creditSum = 0;
    for (Score score : scores) {
      if (!score.consider) continue;
      creditSum += score.credits;
    }
    return creditSum;
  }

}
